package com.cm.tianwt.socks.utils;

import java.io.Closeable;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Map;


public class Utils {
	
	private Utils() {}
	
	
	public static	InetAddress	calcInetIpV4Address( byte[] addr )	{
		
		if(addr==null || addr.length<Configs.ADDRESS_IPV4_LENGTH) return null;
		
		InetAddress	address  = null;
		String addressName = parseAddressForIPV4(addr);
		
		try	{
			address = InetAddress.getByName( addressName );
		}
		catch( UnknownHostException e )	{
			LogUtils.E(""+e);
			e.printStackTrace();
			return null;
		}
		
		return address;
		
	}
	
	public static	InetAddress	calcInetIpV6Address( byte[] addr )	{
		InetAddress	address  = null;
		if(addr==null || addr.length<Configs.ADDRESS_IPV6_LENGTH) return null;
		try	{
			
			String addressName = parseAddressForIPV6(addr);
			address = InetAddress.getByName( addressName );
		}
		catch( UnknownHostException e )	{
			LogUtils.E(""+e);
			e.printStackTrace();
			return null;
		}
		
		return address;
		
	}
	

	public static String parseAddressForIPV4(byte[] addr) {
				
		if( addr.length < Configs.ADDRESS_IPV4_LENGTH )	{
			LogUtils.E( "calcInetAddress() - Invalid length of IP v4 - "+addr.length+" bytes" );	
			return null;
		}
		StringBuilder sb = new StringBuilder();
		for( int i=0; i<Configs.ADDRESS_IPV4_LENGTH; i++ )	{
			sb.append(byte2int( addr[i] ));
			if( i<3 )	sb.append(".");
		}
		return sb.toString();
	}
	
	public static String parseAddressForIPV6(byte[] addr) {
		if( addr.length < Configs.ADDRESS_IPV6_LENGTH )	{
			LogUtils.E( "calcInetAddress() - Invalid length of IP v4 - "+addr.length+" bytes" );	
			return null;
		}
		
		StringBuilder sb = new StringBuilder();
		for( int i=0; i<Configs.ADDRESS_IPV6_LENGTH; i+=2 )	{
			sb.append(byte2Hex( addr,i));
			if( i<14 )	sb.append(":");
		}
		return sb.toString();
	}
	public static InetAddress calcInetHostNameAddress(byte[] addr) {
		
		InetAddress address = null;
		if(addr==null) return null;
		
		StringBuilder sb = new StringBuilder();
		for( int i = 1; i <= addr[0]; i++ )	{
			sb.append((char)addr[i]);
		}
		String hostName = sb.toString();
		
		try	{
			address = InetAddress.getByName( hostName );
		}
		catch( UnknownHostException e )	{
			e.printStackTrace();
			return null;
		}
		return address;
	}
	
	private static String byte2Hex(byte[] addr, int index) {
		return Integer.toHexString(addr[index]) +  Integer.toHexString(addr[index+1]); //小端编码
	}

	public	static int	byte2int( byte b )	{
		int	res = b;
		if( res < 0 ) res = (int)( 0x100 + res );
		return	res;
	}

	
	public static	int	calcPort( byte Hi, byte Lo )	{
		
		return ( (byte2int( Hi ) << 8) | byte2int( Lo ) );	
	}
	
	
	public static	String	iP2Str( InetAddress IP )	{
		if( IP == null )	return "NA/NA";
		
		return	IP.getHostName()+"/"+IP.getHostAddress();
	}
	
	
	public static	String	getSocketInfo( Socket sock )	{
		
		if( sock == null )	return "<NA/NA:0>";
		
		return	"<"+Utils.iP2Str( sock.getInetAddress() )+":"+
				sock.getPort() + ">";
	}
	
	
	
	public	static String	getSocketInfo( DatagramPacket DGP )	{
	
		if( DGP == null )	return "<NA/NA:0>";
		
		return	"<"+Utils.iP2Str( DGP.getAddress() )+":"+
				DGP.getPort() + ">";
	}
	
	
	public static String getCodeDesc( byte code )	{
		
		switch( code )	{
			case 0: return "SUCCESS";
			case 1: return "General SOCKS Server failure";
			case 2: return "Connection not allowed by ruleset";
			case 3: return "Network Unreachable";
			case 4: return "HOST Unreachable";
			case 5: return "Connection Refused";
			case 6: return "TTL Expired";
			case 7: return "Command not supported";
			case 8: return "Address Type not Supported";
			case 9: return "to 0xFF UnAssigned";
			
			case 90: return "Request GRANTED";
			case 91: return "Request REJECTED or FAILED";
			case 92: return "Request REJECTED - SOCKS server can't connect to Identd on the client";
			case 93: return "Request REJECTED - Client and Identd report diff user-ID";		 
					   
			default:	return "Unknown Command";
		}
	}
	
	
	public static synchronized void closeQuiety(Closeable io) {
		if(io==null) return;
		try {
			io.close();
		} catch (IOException ignore) {
			ignore.printStackTrace();
		}
	}

	public static boolean checkVersion(int version,int target) throws IOException {
		if(version!= target) {
			LogUtils.E(String.format("Socks Error,Client Version[%d] is not match ProxyServer' version[%d]",version,target));
			return false;
		}
		return true;
	}

	public static byte[] combineArray(byte[] ... arrays) {
		if(arrays==null || arrays.length==0) return null;
		int sum = 0;
		
		for(int i=0;i<arrays.length;i++) {
			byte[] databytes = arrays[i];
			if(databytes==null) continue;
			sum += databytes.length;
		}
		
		byte[] bufs = new byte[sum];
		int k =0;
		for(int i=0;i<arrays.length;i++) {
			byte[] databytes = arrays[i];
			if(databytes==null) continue;
			for(int j=0;j<databytes.length;j++) {
				bufs[k] = databytes[j];
				k++;
			}
		}
		
		return bufs;
	}
	
	public	static String	getCommandName( int version )	{
		
		switch( version )	{
			case 0x01: return "CONNECT";
			case 0x02: return "BIND";
			case 0x03: return "UDP Association";
			default:	return "Unknown Command";
		}
	
	}

	
	
}
